#include "buildin_cmd.h"
#include "syscall.h"
#include "stdio.h"
#include "string.h"
#include "fs.h"
#include "global.h"
#include "dir.h"
#include "shell.h"
#include "assert.h"

// 将路径 old_abs_path 中的 .. 和 . 转换成实际路径存入 new_abs_path
static void wash_path(char* old_abs_path, char* new_abs_path) {
    assert(old_abs_path[0] == '/');
    char name[MAX_FILE_NAME_LEN] = {0};
    char* sub_path = old_abs_path;
    sub_path = path_parse(sub_path, name);

    if(name[0] == 0) { // 只键入了 /
        new_abs_path[0] = '/';
        new_abs_path[1] = 0;
        return;
    }

    new_abs_path[0] = 0;
    strcat(new_abs_path, "/");
    while(name[0]) {
        if(!strcmp("..", name)) { // 若解析出来的目录是 ..
            char* slash_ptr = strrchr(new_abs_path, '/');
            if(slash_ptr != new_abs_path) { // 若 .. 后还未到达顶层，例如 /a/b .. 后为 /a
                *slash_ptr = 0;
            } else { // 若 .. 后到达了顶层，例如 /a .. 后为 /
                *(slash_ptr + 1) = 0;
            }
        } else if(strcmp(".", name)) { // 若解析出来的目录不是 .
            if(strcmp(new_abs_path, "/")) { // 判断顶层是否为 /
                strcat(new_abs_path, "/"); // 不是，则追加，这个判断是为了避免开头变成 // 的情况
            }
            // 追加目录
            strcat(new_abs_path, name);
        } // 若解析出来的目录是 . 则无需任何操作

        // 继续遍历下一层路径
        memset(name, 0, MAX_FILE_NAME_LEN);
        if(sub_path)
            sub_path = path_parse(sub_path, name);
    }
}

// 将 path 处理成不含 .. 和 . 的绝对路径，保存到 final_path 中，path 是用户键入的
void make_clear_abs_path(char* path, char* final_path) {
    char abs_path[MAX_PATH_LEN] = {0};
    // 线判断输入的是否为绝对路径
    if(path[0] != '/') {
        memset(abs_path, 0, MAX_PATH_LEN);
        // 获取当前层的绝对路径
        if(getcwd(abs_path, MAX_PATH_LEN) != NULL) {
            if(!((abs_path[0] == '/') && (abs_path[1] == 0))) {
                strcat(abs_path, "/");
            }
        }
    }
    // 将键入的路径 path 拼接到当前层的绝对路径后面
    strcat(abs_path, path);
    // 将 abs_path 中的 . or .. 转为不含 . or .. 的绝对路径 final_path
    wash_path(abs_path, final_path);
}

// pwd 命令的内建函数
void buildin_pwd(uint32_t argc, char** argv UNUSED) {
    if(argc != 1) {
        printf("pwd: no argument support!\n");
        return;
    } else {
        if(NULL != getcwd(final_path, MAX_PATH_LEN)) {
            printf("%s\n", final_path);
        } else {
            printf("pwd: get current work directory failed.\n");
        }
    }
}

// cd 命令的内建函数
char* buildin_cd(uint32_t argc, char** argv) {
    if(argc > 2) {
        printf("cd: only support 1 argument!\n");
        return NULL;
    }

    if(argc == 1) { // 若只是键入命令 cd，而没有参数(这里指的路径)，则直接返回根目录
        final_path[0] = '/';
        final_path[1] = 0;
    } else { // 否则就修改工作路径（是在终端显示的那个 [xiaoling@localhost path]）
        make_clear_abs_path(argv[1], final_path);
    }

    // 更该当前用户进程的工作路径
    if(chdir(final_path) == -1) {
        printf("cd: no such directory %s\n", final_path);
        return NULL;
    }

    return final_path;
}

// ls 命令的内建函数
void buildin_ls(uint32_t argc, char** argv) {
    char* pathname = NULL;
    struct stat file_stat;
    memset(&file_stat, 0, sizeof(struct stat));
    bool long_info = false;
    uint32_t arg_path_nr = 0;
    uint32_t arg_idx = 1; // 跨过argv[0],因为argv[0]=ls

    while(arg_idx < argc) {
        if(argv[arg_idx][0] == '-') { // 若是参数，则前缀为 -
            if(!strcmp("-l", argv[arg_idx])) { // 参数 -l
                long_info = true;
            } else if(!strcmp("-h", argv[arg_idx])) { // 参数 -h
                printf("usage: -l list all infomation about the file.\n-h for help\nlist all files in the current dirctory if no option\n");
                return;
            } else { // 只支持 -h -l 两个参数
                printf("ls: invalid option %s\nTry `ls -h' for more information.\n", argv[arg_idx]);
                return;
            }
        } else { // 得到路径参数值
            if(arg_path_nr == 0) {
                pathname = argv[arg_idx];
                arg_path_nr = 1;
            } else {
                printf("ls: only support one path\n");
                return;
            }
        }
        arg_idx++;
    }

    if(pathname == NULL) { // 若没有给明确的路径，则默认当前路径为路径参数
        if(getcwd(final_path, MAX_PATH_LEN) != NULL) {
            pathname = final_path;
        } else {
            printf("ls: getcwd for default path failed\n");
            return;
        }
    } else {
        make_clear_abs_path(pathname, final_path);
        pathname = final_path;
    }

    // 得到目标文件的属性
    if(stat(pathname, &file_stat) == -1) {
        printf("ls: cannot access %s: No such file or directory\n", pathname);
        return;
    }

    // 判断文件类型
    if(file_stat.st_filetype == FT_DIRECTORY) { // 是目录
        struct dir* dir = opendir(pathname);
        struct dir_entry* dir_e = NULL;
        char sub_pathname[MAX_PATH_LEN] = {0};
        uint32_t pathname_len = strlen(pathname);
        uint32_t last_char_idx = pathname_len - 1;
        memcpy(sub_pathname, pathname, pathname_len);

        // 保证路径为 /a/b/c/ 而不是 /a/b/c 这是为了便于后面 stat 读取
        if(sub_pathname[last_char_idx] != '/') {
            sub_pathname[pathname_len] = '/';
            pathname_len++;
        }
        rewinddir(dir);
        if(long_info) {
            char ftype;
            printf("total: %d\n", file_stat.st_size);
            while((dir_e = readdir(dir))) {
                ftype = 'd';
                if(dir_e -> f_type == FT_REGULAR) ftype = '-';
                sub_pathname[pathname_len] = 0;
                strcat(sub_pathname, dir_e -> filename); // 拼接文件名到路径后面
                memset(&file_stat, 0, sizeof(struct stat));
                if(stat(sub_pathname, &file_stat) == -1) {
                    printf("ls: cannot access %s: No such file or directory\n", dir_e->filename);
                    return;
                }
                printf("%c  %d  %d  %s\n", ftype, dir_e->i_no, file_stat.st_size, dir_e->filename);
            }
        } else {
            while((dir_e = readdir(dir))) {
                printf("%s ", dir_e -> filename);
            }
            printf("\n");
        }
        closedir(dir);
    } else {
        if(long_info) 
            printf("-  %d  %d  %s\n", file_stat.st_ino, file_stat.st_size, pathname);
        else
            printf("%s\n", pathname);
    }
}

// ps 命令内建函数
void buildin_ps(uint32_t argc, char** argv UNUSED) {
    if(argc != 1) {
        printf("ps: no argument support!\n");
        return;
    }
    ps();
}

// clear 命令内建函数
void buildin_clear(uint32_t argc, char** argv UNUSED) {
   if (argc != 1) {
      printf("clear: no argument support!\n");
      return;
   }
   clear();
}

// mkdir 命令内建函数
int32_t buildin_mkdir(uint32_t argc, char** argv) {
    int32_t ret = -1;
    if(argc != 2) {
        printf("mkdir: only support 1 argument!\n");
    } else {
        make_clear_abs_path(argv[1], final_path);
        if(!strcmp("/", final_path)) return ret; // 不能创建根目录
        if(mkdir(final_path) == 0) 
            ret = 0;
        else
            printf("mkdir: create directory %s failed.\n", argv[1]);
    }
    return ret;
}

// rmdir 命令内建函数
int32_t buildin_rmdir(uint32_t argc, char** argv) {
    int32_t ret = -1;
    if(argc != 2) {
        printf("rmdir: only support 1 argument!\n");
    } else {
        make_clear_abs_path(argv[1], final_path);
        if(!strcmp("/", final_path)) return ret; // 不能删除根目录
        if(rmdir(final_path) == 0)
            ret = 0;
        else
            printf("rmdir: remove %s failed.\n", argv[1]);
    }
    return ret;
}

// rm 命令内建函数
int32_t buildin_rm(uint32_t argc, char** argv) {
    int32_t ret = -1;
    if(argc != 2) {
        printf("rm: only support 1 argument!\n");
    } else {
        make_clear_abs_path(argv[1], final_path);
        if(!strcmp("/", final_path)) return ret; // 不能删除根目录
        if(unlink(final_path) == 0)
            ret = 0;
        else
            printf("rm: delete %s failed.\n", argv[1]);
    }
    return ret;
}
